/*******************************************************************************
 * Copyright 2013 Mindaugas Idzelis
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/
package pure.balancer;

import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Lists.transform;
import incontrol.bf3.protocol.events.PlayerStatsEvent;
import incontrol.bf3.protocol.events.RoundOverEvent;
import incontrol.bf3.protocol.events.TeamScoresEvent;
import incontrol.bf3.state.PlayerList;
import incontrol.bf3.state.PlayerState;
import incontrol.core.Config;
import incontrol.core.PartitionAlg;
import incontrol.core.group.Group;
import incontrol.core.group.GroupRequest;
import incontrol.core.group.SinglePlayerGroup;
import incontrol.core.group.WeightedGroup;
import incontrol.core.protocol.responses.GenericResponse;
import incontrol.core.weight.WeightRequest;
import incontrol.plugin.Plugin;
import incontrol.plugin.protocol.INSubscribe;

import java.math.RoundingMode;
import java.util.Collection;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;
import com.google.common.math.IntMath;

public class PureBalancer extends Plugin {

	@INSubscribe
	public void handlePlayerJoinEvent(RoundOverEvent event) {

	}

	@INSubscribe
	public void handlePlayerLeaveEvent(RoundOverEvent event) {

	}

	@INSubscribe
	public void handleKillEvent(RoundOverEvent event) {

	}

	@INSubscribe
	public void handlePlayerStatsEvent(PlayerStatsEvent event) {

	}

	@INSubscribe
	public void handleTeamScoreEvent(TeamScoresEvent event) {

	}

	@INSubscribe
	public void handleGroupRequest(GroupRequest req) {
		List<Group> groups = newArrayList();
		PlayerList playerList = getPlayerList();
		for (PlayerState player : playerList.getPlayers()) {
			groups.add(new SinglePlayerGroup(player.name));
		}
		sendResponse(req, new GenericResponse(groups));
	}

	/**
	 * temp - will be moved to server
	 * 
	 * @param req
	 */
	@INSubscribe
	public void handleWeights(WeightRequest req) {
		PlayerList playerList = getPlayerList();
		List<Group> groups = req.getGroups();
		List<WeightedGroup> weighteds = newArrayList();
		for (Group g : groups) {
			if (g.getPlayers().size() == 0) {
				weighteds.add(new WeightedGroup(g, 0));
			} else {
				int score = 0;
				List<String> players = g.getPlayers();
				for (String name : players) {
					score += playerList.byName().get(name).score;
				}
				weighteds.add(new WeightedGroup(g, IntMath.divide(score,
						players.size(), RoundingMode.HALF_UP)));
			}
		}
		sendResponse(req, new GenericResponse(weighteds));
	}

	@Override
	public void init(Config config) {
		getEventBus().register(this);
		Timer t = new Timer();
		t.scheduleAtFixedRate(new TimerTask() {

			@Override
			public void run() {
				balance();
			}
		}, 5000, 5000);
	}

	/**
	 * each provider returns a list of groups
	 * 
	 * @return
	 */
	public List<List<Group>> getGroups() {
		List<GenericResponse> groups = sendRequest(new GroupRequest());
		return transform(groups, new Function<GenericResponse, List<Group>>() {
			@SuppressWarnings("unchecked")
			public List<Group> apply(GenericResponse input) {
				return (List<Group>) input.getObject();
			};
		});
	}

	@INSubscribe
	public List<List<WeightedGroup>> getWeights(List<Group> group) {
		List<GenericResponse> groups = sendRequest(new WeightRequest(group));
		return transform(groups,
				new Function<GenericResponse, List<WeightedGroup>>() {
					@SuppressWarnings("unchecked")
					public List<WeightedGroup> apply(GenericResponse input) {
						return (List<WeightedGroup>) input.getObject();
					};
				});
	}

	private void balance() {

		// do extension processing here

		// go in order of group provider priorities - group providers do not
		// provide a complete grouping. If a member is put into conflicting
		// groups by different providers, prefer the higher priority provider.

		// after all groups are done, resolve multi-group conflicts by fill in
		// group to max capacity using a greedy algorithm, then partition the
		// groups according to ckk algorithm.

		// Currently: just use ckk

		List<Group> groups = getGroups().get(0);

		// diff weight providers might do diff things
		List<WeightedGroup> weighed = getWeights(groups).get(0);

		List<List<WeightedGroup>> ckk = PartitionAlg.ckk(weighed);

	}

	public static void main(String[] args) {
		List<Integer> list = newArrayList();
		for (int i = 0; i < 64; i++) {
			list.add(i);
		}
		Collection<List<Integer>> permutations = Collections2
				.permutations(list);
		for (List<Integer> list2 : permutations) {

		}
		System.out.print(permutations.size());
	}

}
